#include "art/art.h"
#include "audio_assets.h"
#include <getopt.h>
#include <locale.h>
#include <ncurses.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>

#define VERSION "1.1.1"
#define STATIC_V1_X 15
#define STATIC_V1_Y 110
#define STATIC_V1_RY 38
#define ANIMATED_V1_X 30
#define ANIMATED_V1_Y 187
#define ANIMATED_V1_RY 62
#define ANIMATED_V2_X 30
#define ANIMATED_V2_Y 189
#define ANIMATED_V2_RY 62
#define ANIMATED_V3_X 30
#define ANIMATED_V3_Y 189
#define ANIMATED_V3_RY 62
#define ANIMATED_MY 189
#define STATIC_VERSION 1
#define ANIMATED_VERSION 3
#define MAX_LENGTH 30

bool debug_mode = false;

// 音频控制变量（新增loop_count控制循环次数）
volatile int stop_audio = 0;
const AudioAsset *g_selected_audio = NULL;
int pipefd[2];
pid_t ffplay_pid = -1;
int loop_count = 1; // 默认播放1次（不循环），0=无限循环，>0=循环N次

static int parse_loop_count(const char *optarg) {
  if (optarg == NULL)
    return 0; // 无参数默认无限循环
  char *endptr;
  long val = strtol(optarg, &endptr, 10);
  if (*endptr != '\0' || val < 0) { // 非数字或负数
    fprintf(stderr, "[警告] 无效的循环次数：%s，默认不循环\n", optarg);
    return 1;
  }
  return (int)val;
}

void random_select_mp3() {
  int rand_idx = rand() % AUDIO_ASSET_COUNT;
  g_selected_audio = &audio_assets[rand_idx];
  if (debug_mode) {
    printf("[音频] 选中嵌入式音频（索引：%d，大小：%u字节）\n", rand_idx,
           g_selected_audio->len);
  }
}

/**
 * 单次音频传输：向ffplay传输一次音频数据
 * 返回值：0=成功，-1=失败
 */
static int audio_single_play(int current_loop) {
  // 1. 创建管道
  if (pipe(pipefd) == -1) {
    perror("[错误] 管道创建失败");
    return -1;
  }

  // 2. 创建ffplay子进程
  ffplay_pid = fork();
  if (ffplay_pid == -1) {
    perror("[错误] 创建子进程失败");
    close(pipefd[0]);
    close(pipefd[1]);
    return -1;
  }

  if (ffplay_pid == 0) { // 子进程：启动ffplay
    close(pipefd[1]);
    if (dup2(pipefd[0], STDIN_FILENO) == -1) {
      perror("[子进程错误] 重定向标准输入失败");
      exit(EXIT_FAILURE);
    }
    close(pipefd[0]);

    // ffplay参数：-nodisp无窗口，-autoexit播放完退出，-loglevel error隐藏日志
    execlp("ffplay", "ffplay", "-hide_banner", "-nodisp", "-autoexit",
           "-loglevel", "error", "-i", "-", NULL);

    perror("[子进程错误] 启动ffplay失败（请安装ffmpeg）");
    exit(EXIT_FAILURE);
  } else { // 父进程：传输数据
    close(pipefd[0]);
    const size_t chunk_size = 8192;
    size_t total_written = 0;
    const uint8_t *data = g_selected_audio->data;
    const size_t total_len = g_selected_audio->len;
    if (debug_mode) {
      printf("[音频] 开始传输数据（第 %d 次播放，总大小：%lu字节）\n",
             current_loop, total_len);
    }
    while (total_written < total_len && !stop_audio) {
      size_t write_len = (total_len - total_written) > chunk_size
                             ? chunk_size
                             : (total_len - total_written);
      ssize_t ret = write(pipefd[1], data + total_written, write_len);
      if (ret == -1) {
        perror("[错误] 管道写入失败");
        close(pipefd[1]);
        return -1;
      }
      total_written += ret;
    }

    close(pipefd[1]);
    if (debug_mode) {
      printf("[音频] 数据传输完成（已写：%zu字节）\n", total_written);
    }

    // 等待当前ffplay播放结束
    int status;
    waitpid(ffplay_pid, &status, 0);
    ffplay_pid = -1;
    if (debug_mode) {
      printf("[音频] 第 %d 次播放结束\n", current_loop);
    }
  }
  return 0;
}

/**
 * 音频播放线程：支持循环播放
 */
void *audio_play_thread(void *arg) {
  (void)arg;
  int current_loop = 0;
  // 循环逻辑：loop_count=0无限循环，否则循环指定次数
  while ((loop_count == 0 || current_loop < loop_count) && !stop_audio) {
    current_loop++;
    if (audio_single_play(current_loop) == -1) {
      fprintf(stderr, "[音频错误] 第 %d 次播放失败，终止循环\n", current_loop);
      break;
    }
  }

  stop_audio = 1;
  if (debug_mode) {
    printf("[音频] 循环播放结束（共播放 %d 次）\n", current_loop);
  }
  return NULL;
}

// -------------------------- 信号处理 --------------------------
void sig_handler(int sig) {
  (void)sig;
  stop_audio = 1;
  if (ffplay_pid != -1) {
    kill(ffplay_pid, SIGTERM);
  }
  endwin();
  exit(0);
}

void init() {
  setlocale(LC_ALL, "");
  initscr();
  cbreak();
  noecho();
  keypad(stdscr, TRUE);
  curs_set(0);
  timeout(-1);
  signal(SIGINT, sig_handler);
  signal(SIGTERM, sig_handler);
}

void help() {
  printf("Make cute Momoi from Blue Archive say something!!!\n"
         "operations:\n"
         "    -h, --help                          Display this help message\n"
         "    -v, --version                       Show version information\n"
         "    -a <version> <text>                 Cool animated version of "
         "cute Momoi (default version 1)\n"
         "    -f <text>                           Freestyle Momoi animation\n"
         "    -s <version> <text>                 Display static version of "
         "cute Momoi (default version 1)\n"
         "    -l, --list                          List available versions for "
         "Momoi ASCII arts\n"
         "    -L, --loop <count>                  Loop audio playback "
         "(0=infinite, default: 1)\n"
         "    <text>                              Text that cute Momoi will "
         "say!!! (default static version 1)\n"
         "Note: Uses ffplay for audio (requires ffmpeg installed)\n");
}

void version() {
  printf("Momoisay v%s\n"
         "License: GPL-3.0 License\n"
         "With embedded audio (using ffplay, support loop playback)\n",
         VERSION);
}

int stoi(char *s) {
  int num = 0, cnt = 0;
  while (*s != '\0') {
    num *= 10;
    if (!('0' <= *s && *s <= '9') || (!cnt && *s == '0')) {
      return -1;
    }
    cnt++;
    num += *s - '0';
    s++;
  }
  return num;
}

int randomizer(int min, int max) { return rand() % (max - min + 1) + min; }

int randint(const int arr[], int size) { return arr[rand() % size]; }

int get_line(char *argv[], int start, int end) {
  int lines = 0, cnt = 0;
  if (end - start)
    lines++;
  for (int i = start; i < end; i++) {
    char *str = argv[i];
    while (*str != '\0') {
      if (cnt >= MAX_LENGTH) {
        cnt = 0;
        lines++;
      }
      cnt++;
      str++;
    }
    cnt++;
  }
  return lines;
}

char **create_canvas(int x, int y) {
  char **canvas = (char **)malloc(x * sizeof(char *));
  for (int i = 0; i < x; i++) {
    canvas[i] = (char *)calloc(y + 1, sizeof(char));
  }
  return canvas;
}

void print_canvas(char **canvas, int x, int px, int py) {
  for (int i = 0; i < x; i++) {
    mvprintw(py + i, px, "%s", canvas[i]);
  }
  refresh();
}

void free_canvas(char **canvas, int x) {
  if (canvas == NULL)
    return;
  for (int i = 0; i < x; i++) {
    free(canvas[i]);
  }
  free(canvas);
}

int textlen(char *argv[], int start, int end) {
  int length = 0;
  for (int i = start; i < end; i++)
    length += strlen(argv[i]) + 1;
  if (length - 1 > MAX_LENGTH)
    return MAX_LENGTH;
  return length - 1;
}

void construct_v1(const char **art[], char *argv[], int *intervals, int frames,
                  int x, int y, int ry, int length, int lines, int start,
                  int end, int round) {
  int current_frame = 0;
  while (round != 0) {
    int cnt = 0, pt1 = (x + 1) / 2, pt2 = ((x + 1) / 2) + 1, pts = 3 + y,
        ptt = pts;
    char **canvas = create_canvas(x, y + length);
    clear();
    int terminal_height = LINES;
    int terminal_width = COLS;
    int px = (terminal_width - ry - length) / 2;
    int py = (terminal_height - x) / 2;
    for (int i = 0; i < x; i++) {
      int len = strlen(art[current_frame][i]);
      for (int j = 0; j < y + length; j++) {
        if (j < len) {
          canvas[i][j] = art[current_frame][i][j];
        } else if (canvas[i][j] == '\0') {
          canvas[i][j] = ' ';
        }
        if (!i && length) {
          if (j == y) {
            canvas[pt1--][j] = '/';
            canvas[pt2++][j] = '\\';
          } else if (j - 1 == y) {
            for (int cnt = 0; cnt < lines / 2; cnt++) {
              canvas[pt1--][j] = '|';
              canvas[pt2++][j] = '|';
            }
            pt2--;
          } else if (j + 1 == y + length) {
            for (int k = ++pt1; k <= pt2; k++) {
              canvas[k][j] = '|';
            }
            pt1++;
          } else {
            canvas[pt1][j] = '_';
            canvas[pt2][j] = '_';
          }
        }
      }
      if (!cnt && (length || lines)) {
        for (int j = start; j < end; j++) {
          char *str = argv[j];
          while (*str != '\0') {
            if (cnt >= MAX_LENGTH) {
              pt1++;
              pts = ptt;
              cnt = 0;
            }
            canvas[pt1][pts++] = *str;
            cnt++;
            str++;
          }
          if (cnt > MAX_LENGTH)
            continue;
          canvas[pt1][pts++] = ' ';
          cnt++;
        }
      }
      cnt = 1;
    }
    print_canvas(canvas, x, px, py);
    usleep(intervals[current_frame++]);
    free_canvas(canvas, x);
    if (current_frame == frames) {
      current_frame = 0;
      if (round > 0)
        round--;
    }
  }
}

void construct_v2(const char **art[], char *argv[], int *intervals, int frames,
                  int x, int y, int ry, int length, int lines, int start,
                  int end, int reped, int repmin, int repmax, int round) {
  int current_frame = 0, replap = randomizer(repmin, repmax);
  while (round != 0) {
    int cnt = 0, pt1 = (x + 1) / 2, pt2 = ((x + 1) / 2) + 1, pts = 3 + y,
        ptt = pts;
    char **canvas = create_canvas(x, y + length);
    clear();
    int terminal_height = LINES;
    int terminal_width = COLS;
    int px = (terminal_width - ry - length) / 2;
    int py = (terminal_height - x) / 2;
    for (int i = 0; i < x; i++) {
      int len = strlen(art[current_frame][i]);
      for (int j = 0; j < y + length; j++) {
        if (j < len) {
          canvas[i][j] = art[current_frame][i][j];
        } else if (canvas[i][j] == '\0') {
          canvas[i][j] = ' ';
        }
        if (!i && length) {
          if (j == y) {
            canvas[pt1--][j] = '/';
            canvas[pt2++][j] = '\\';
          } else if (j - 1 == y) {
            for (int cnt = 0; cnt < lines / 2; cnt++) {
              canvas[pt1--][j] = '|';
              canvas[pt2++][j] = '|';
            }
            pt2--;
          } else if (j + 1 == y + length) {
            for (int k = ++pt1; k <= pt2; k++) {
              canvas[k][j] = '|';
            }
            pt1++;
          } else {
            canvas[pt1][j] = '_';
            canvas[pt2][j] = '_';
          }
        }
      }
      if (!cnt && (length || lines)) {
        for (int j = start; j < end; j++) {
          char *str = argv[j];
          while (*str != '\0') {
            if (cnt >= MAX_LENGTH) {
              pt1++;
              pts = ptt;
              cnt = 0;
            }
            canvas[pt1][pts++] = *str;
            cnt++;
            str++;
          }
          if (cnt > MAX_LENGTH)
            continue;
          canvas[pt1][pts++] = ' ';
          cnt++;
        }
      }
      cnt = 1;
    }
    print_canvas(canvas, x, px, py);
    usleep(intervals[current_frame]);
    if (current_frame == reped) {
      if (replap == 0) {
        replap = randomizer(repmin, repmax);
        current_frame++;
      } else {
        current_frame = 0;
        replap--;
      }
    } else {
      current_frame++;
    }
    free_canvas(canvas, x);
    if (current_frame == frames) {
      current_frame = 0;
      if (round > 0)
        round--;
    }
  }
}

void construct_freestyle(char *argv[], int length, int lines, int start,
                         int end) {
  int select = randomizer(0, ANIMATED_VERSION - 1);
  while (1) {
    if (stop_audio)
      break;
    if (select == 0) {
      int frame[5] = {150000, 75000, 150000, 150000, 75000};
      construct_v1(momoi_animated_v1, argv, frame, 5, ANIMATED_V1_X,
                   ANIMATED_V1_Y, ANIMATED_V1_RY, length, lines, start, end,
                   randomizer(3, 5));
      select = 2;
    } else if (select == 1) {
      int frame[7] = {70000, 70000, 70000, 1500000, 70000, 70000, 70000};
      construct_v2(momoi_animated_v2, argv, frame, 7, ANIMATED_V2_X,
                   ANIMATED_V2_Y, ANIMATED_V2_RY, length, lines, start, end, 1,
                   5, 13, randomizer(1, 3));
      select = randint((int[]){0, 2}, 2);
    } else if (select == 2) {
      int frame[8] = {70000, 70000, 70000, 70000, 70000, 70000, 70000, 70000};
      construct_v1(momoi_animated_v3, argv, frame, 8, ANIMATED_V3_X,
                   ANIMATED_V3_Y, ANIMATED_V3_RY, length, lines, start, end,
                   randomizer(3, 5));
      select = randint((int[]){1}, 1);
    }
  }
}

int main(int argc, char *argv[]) {
  srand(time(NULL));

  int option;
  int mode = 0;
  int ctl = 0;
  int argctl = 0;
  int animated_version = 1;
  int static_version = 1;
  pthread_t audio_thread;

  // 新增--loop选项（长选项）和-L（短选项）
  struct option long_options[] = {
      {"help", no_argument, NULL, 'h'},
      {"version", no_argument, NULL, 'v'},
      {"list", no_argument, NULL, 'l'},
      {"loop", required_argument, NULL, 'L'}, // 新增循环长选项
      {NULL, 0, NULL, 0}};

  while ((option = getopt_long(argc, argv, "hvla::s::f::L:", long_options,
                               NULL)) != -1) {
    switch (option) {
    case 'h':
      help();
      return 0;
    case 'v':
      version();
      return 0;
    case 'l':
      printf("static: ");
      for (int i = 1; i <= STATIC_VERSION; i++) {
        printf("%d ", i);
      }
      printf("\n");
      printf("animated: ");
      for (int i = 1; i <= ANIMATED_VERSION; i++) {
        printf("%d ", i);
      }
      printf("\n");
      return 0;
    case 'a':
      mode = 1;
      if (!ctl)
        argctl = 0;
      if (argc <= 2)
        break;
      optarg = argv[2];
      if (optarg && 0 < stoi(optarg) && stoi(optarg) <= ANIMATED_VERSION &&
          !ctl) {
        ctl = 1;
        argctl = 1;
        animated_version = stoi(optarg);
      }
      break;
    case 's':
      mode = 0;
      if (!ctl)
        argctl = 0;
      if (argc <= 2)
        break;
      optarg = argv[2];
      if (optarg && 0 < stoi(optarg) && stoi(optarg) <= STATIC_VERSION &&
          !ctl) {
        ctl = 1;
        argctl = 1;
        static_version = stoi(optarg);
      }
      break;
    case 'f':
      mode = 2;
      if (!ctl)
        argctl = 0;
      if (argc <= 2)
        break;
      optarg = argv[2];
      break;
    case 'L': // 处理循环参数
      loop_count = parse_loop_count(optarg);
      if (debug_mode) {
        printf("[音频] 循环播放设置：%s（%d 次）\n",
               loop_count == 0 ? "无限循环" : "有限循环",
               loop_count == 0 ? 999 : loop_count);
      }
      break;
    default:
      help();
      return 0;
    }
  }

  // 初始化嵌入式音频与播放线程
  random_select_mp3();
  if (pthread_create(&audio_thread, NULL, audio_play_thread, NULL) != 0) {
    fprintf(stderr, "[音频错误] 无法创建播放线程\n");
    return 1;
  }

  // 初始化终端与动画
  init();

  // 原有动画逻辑保持不变
  if (mode == 2) {
    int length = 0, lines = 0;
    length = 5 + textlen(argv, optind + argctl, argc);
    lines = get_line(argv, optind + argctl, argc);
    if (length <= 5)
      length = 0;
    if (lines <= 10) {
      if (lines & 1)
        lines++;
      construct_freestyle(argv, length, lines, optind + argctl, argc);
      stop_audio = 1;
      pthread_join(audio_thread, NULL);
      return 0;
    }
  } else if (mode == 1) {
    int length = 0, lines = 0;
    length = 5 + textlen(argv, optind + argctl, argc);
    lines = get_line(argv, optind + argctl, argc);
    if (animated_version == 1) {
      if (length <= 5)
        length = 0;
      if (lines <= 30) {
        if (lines & 1)
          lines++;
        int frame[5] = {150000, 75000, 150000, 150000, 75000};
        construct_v1(momoi_animated_v1, argv, frame, 5, ANIMATED_V1_X,
                     ANIMATED_V1_Y, ANIMATED_V1_RY, length, lines,
                     optind + argctl, argc, -1);
      }
    } else if (animated_version == 2) {
      if (length <= 5)
        length = 0;
      if (lines <= 30) {
        if (lines & 1)
          lines++;
        int frame[7] = {70000, 70000, 70000, 1500000, 70000, 70000, 70000};
        construct_v2(momoi_animated_v2, argv, frame, 7, ANIMATED_V2_X,
                     ANIMATED_V2_Y, ANIMATED_V2_RY, length, lines,
                     optind + argctl, argc, 1, 5, 13, -1);
      }
    } else if (animated_version == 3) {
      if (length <= 5)
        length = 0;
      if (lines <= 30) {
        if (lines & 1)
          lines++;
        int frame[8] = {70000, 70000, 70000, 70000, 70000, 70000, 70000, 70000};
        construct_v1(momoi_animated_v3, argv, frame, 8, ANIMATED_V3_X,
                     ANIMATED_V3_Y, ANIMATED_V3_RY, length, lines,
                     optind + argctl, argc, -1);
      }
    }
    stop_audio = 1;
    pthread_join(audio_thread, NULL);
  } else if (mode == 0) {
    if (static_version == 1) {
      int length = 0, lines = 0;
      length = 5 + textlen(argv, optind + argctl, argc);
      lines = get_line(argv, optind + argctl, argc);
      if (length <= 5)
        length = 0;
      if (lines <= 10) {
        if (lines & 1)
          lines++;
        int frame[1] = {75000};
        construct_v1(momoi_static_v1, argv, frame, 1, STATIC_V1_X, STATIC_V1_Y,
                     STATIC_V1_RY, length, lines, optind + argctl, argc, -1);
        stop_audio = 1;
        pthread_join(audio_thread, NULL);
        return 0;
      }
    }
  }

  stop_audio = 1;
  pthread_join(audio_thread, NULL);
  endwin();
  return 0;
}
